package com.content.scaffold.common.mybatis;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.content.scaffold.common.utils.BeanConvertUtils;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;

import java.io.Serial;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author xiaojl
 */
@Setter
@Getter
public class PageParams<T> extends Page<T> implements Serializable {
    @Serial
    private static final long serialVersionUID = -1710273706052960025L;
    private static final String SIZE = "size";
    private static final int MAX_LIMIT = 999;
    private int page;
    private int limit;
    private String sort;
    @Getter
    private String order;
    private Map<String, Object> requestMap;
    private String orderBy;
    private LinkedList<OrderItem> orders;

    public PageParams() {
        this(1, 10);
        this.requestMap = Maps.newHashMap();
    }

    public PageParams(Map map) {
        this(map, false);
    }

    public PageParams(Map map, boolean disableSort) {
        this.requestMap = Maps.newHashMap();
        if (map == null) {
            map = Maps.newHashMap();
        }

        // 安全转换 page
        try {
            this.page = Integer.parseInt(String.valueOf(map.getOrDefault("page", 1)));
        } catch (NumberFormatException e) {
            this.page = 1;
        }
        // 安全转换 limit
        try {
            this.limit = Integer.parseInt(String.valueOf(map.getOrDefault(SIZE, map.getOrDefault("limit", 10))));
        } catch (NumberFormatException e) {
            this.limit = 10;
        }

        // 限制最大 limit
        if (this.limit > MAX_LIMIT) {
            this.limit = MAX_LIMIT;
        }

        super.setCurrent(this.page);
        super.setSize(this.limit);

        map.remove("page");
        map.remove(SIZE);
        map.remove("limit");

        if (!disableSort) {
            resetOrder(map);
        }

        this.requestMap.putAll(map);
    }

    /**
     * 设置排序
     */
    private void resetOrder(Map map) {
        this.sort = com.content.scaffold.common.utils.StringUtils.camelToUnderline((String) map.getOrDefault("sort", "created_date"));
        this.order = (String) map.getOrDefault("order", "desc");
        map.remove("sort");
        map.remove("order");

        if (StringUtils.isNotBlank(this.sort) && StringUtils.isNotBlank(this.order)) {
            String[] sortSplit = this.sort.split(",");
            String[] orderSplit = this.order.split(",");

            if(sortSplit.length == orderSplit.length){
                for (int i = 0; i < sortSplit.length; i++) {
                    String sortName = sortSplit[i];
                    String orderName = orderSplit[i];
                    OrderItem orderItem = "asc".equalsIgnoreCase(orderName)
                            ? OrderItem.asc(sortName)
                            : OrderItem.desc(sortName);
                    this.addOrder(orderItem);
                }
            }
        }
    }

    public PageParams(int page, int limit) {
        this(page, limit, "", "");
    }

    public PageParams(int page, int limit, String sort, String order) {
        this.page = Math.max(1, page);
        this.limit = Math.min(Math.max(1, limit), MAX_LIMIT);
        this.sort = sort;
        this.order = order;
        this.requestMap = Maps.newHashMap();

        super.setCurrent(this.page);
        super.setSize(this.limit);
    }

    public int getPage() {
        if (this.page <= 0) {
            this.page = 1;
        }
        return this.page;
    }

    public int getLimit() {
        if (this.limit > MAX_LIMIT) {
            this.limit = MAX_LIMIT;
        }
        return this.limit;
    }

    public String getOrderBy() {
        if (StringUtils.isBlank(this.order)) {
            this.order = "asc";
        }

        if (StringUtils.isNotBlank(this.sort)) {
            this.setOrderBy(String.format("%s %s", com.content.scaffold.common.utils.StringUtils.camelToUnderline(this.sort), this.order));
        }

        return this.orderBy;
    }

    public <R> R mapToObject(Class<R> t) throws Exception {
        return BeanConvertUtils.mapToObject(this.requestMap, t);
    }

    public <R> PageParams<R> convertNewPage(Function<T, R> convertFn) {
        PageParams<R> pageParams = new PageParams<>();
        pageParams.setPage(this.getPage());
        pageParams.setLimit(this.getLimit());
        pageParams.setTotal(this.getTotal());
        if (!this.getRecords().isEmpty()) {
            pageParams.setRecords(this.getRecords().stream().map(convertFn).collect(Collectors.toList()));
        }
        return pageParams;
    }
}
